/*
 * CBombManager.cpp
 *
 *      Author: grifos
 */

#include "CBombManager.h"
CBombManager::CBombManager(CMapNode* m, CJoueur * j, osg::Group * root,int glLighting) {
	_map = m;
	_joueur = j;
	_root = root;
	_grabed = false;
	_nbBomb = 0;
	_blowGeode = new osg::Geode();
	_blowRadius = 0.2f;
	_safePoint.set(1.0f,-20.0f,_map->calculHeight(osg::Vec3d(1.0f,-20.0f,0.0f)).z());
	_bombPos[0][0] = -25.94f;
	_bombPos[0][1] = 4.16f;
	_bombPos[0][2] = _map->calculHeight(osg::Vec3d(_bombPos[0][0],_bombPos[0][1],0)).z()+1.0;
	_bombPos[1][0] = -11.2851f;
	_bombPos[1][1] = 29.8063f;
	_bombPos[1][2] = _map->calculHeight(osg::Vec3d(_bombPos[1][0],_bombPos[1][1],0)).z()+1.0;
	_bombPos[2][0] = 66.85f;
	_bombPos[2][1] = 10.704f;
	_bombPos[2][2] = _map->calculHeight(osg::Vec3d(_bombPos[2][0],_bombPos[2][1],0)).z()+1.0;
	_bombPos[3][0] = -98.11f;
	_bombPos[3][1] = 4.16f;
	_bombPos[3][2] = _map->calculHeight(osg::Vec3d(_bombPos[3][0],_bombPos[3][1],0)).z()+1.0;
	_bombPos[4][0] = -54.0793f;
	_bombPos[4][1] = -36.2248f;
	_bombPos[4][2] = _map->calculHeight(osg::Vec3d(_bombPos[4][0],_bombPos[4][1],0)).z()+1.0;
	_bombBlowed = false;
	_timeSinceBlow = 0.0f;
	_blowPos = new osg::MatrixTransform();
	_bombAttribute = new osg::StateSet();
	_bombAttribute->setMode(glLighting,osg::StateAttribute::OFF);
	_blowGeode->setStateSet(_bombAttribute.get());

	// Le beacon
	_bombBeacon = new osg::MatrixTransform;
	osg::ref_ptr<osg::MatrixTransform> rot = new osg::MatrixTransform;
	osg::ref_ptr<osg::Billboard> BillBoard = new osg::Billboard();
	BillBoard->setMode(osg::Billboard::AXIAL_ROT);
	BillBoard->setAxis(osg::Vec3(0.0f,0.0f,1.0f));
	BillBoard->setNormal(osg::Vec3(0.0f,-1.0f,0.0f));

	// La texture et le state set du beacon
	osg::Texture2D *beaconTexture = new osg::Texture2D;
	beaconTexture->setImage(osgDB::readImageFile("./ressource/beacon.png"));

	osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
	alphaFunc->setFunction(osg::AlphaFunc::GEQUAL,0.05f);

	osg::StateSet* billBoardStateSet = new osg::StateSet;

	billBoardStateSet->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
	billBoardStateSet->setTextureAttributeAndModes
		  (0, beaconTexture, osg::StateAttribute::ON );
	billBoardStateSet->setAttributeAndModes
		  (new osg::BlendFunc, osg::StateAttribute::ON );
	osg::AlphaFunc* alphaFunction = new osg::AlphaFunc;
	alphaFunction->setFunction(osg::AlphaFunc::GEQUAL,0.05f);
	billBoardStateSet->setAttributeAndModes( alphaFunc, osg::StateAttribute::ON );

	// On crée le drawable
	osg::Drawable* beaconDrawable = createBeaconDrawable( 4.0f, billBoardStateSet);

	BillBoard->addDrawable(beaconDrawable);
	// On ajoute la rot au beacon
	rot->addChild(BillBoard.get());
	rot->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(0.0f),1.0f,0.0f,0.0f));
	_bombBeacon->addChild(rot.get());

}

CBombManager::~CBombManager() {
	// TODO Auto-generated destructor stub
}

void CBombManager::addBomb()
{
	_bombBlowed = false;
	delete _bomb;
	std::string model("./ressource/Bomb OSG.3ds");
#if CAVE== 1
	_bomb = new CObject3d(0.0f,0.0f,0.0f,0.0f,model,0.8);
#else
	_bomb = new CObject3d(0.0f,0.0f,0.0f,0.0f,model,0.7);
#endif
	_map->removeBomb();
	_grabed = false;
	_bomb->setPosition(_bombPos[_nbBomb][0],_bombPos[_nbBomb][1],_bombPos[_nbBomb][2]);
	_bombBeacon->setMatrix(osg::Matrix::translate(osg::Vec3(_bombPos[_nbBomb][0],_bombPos[_nbBomb][1],_bombPos[_nbBomb][2]+9.0f)));
	_map->addBomb(_bomb->getNode());
	_map->addBomb(_bombBeacon.get());
	_nbBomb++;
}

void CBombManager::blowBomb()
{
	_blowPos->setMatrix(osg::Matrix::translate(getBombPosInMap()));
	osg::ref_ptr<osg::Sphere> unitSphere;
	osg::ref_ptr<osg::ShapeDrawable> unitSphereDrawable;
	unitSphere = new osg::Sphere( osg::Vec3(0.0f,0.0f,0.0f), _blowRadius);
	unitSphereDrawable = new osg::ShapeDrawable(unitSphere.get());
	_blowGeode->addDrawable(unitSphereDrawable.get());
	osg::MatrixTransform* rot = new osg::MatrixTransform;
	rot->addChild(_blowGeode.get());
	_blowPos->addChild(rot);
	// On rajoute un halo
	if(_grabed==false)
	{
		rot->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(-90.0f),1.0f,0.0f,0.0f));
		_map->addBomb(_blowPos);
	}
	else
	{
//		rot->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(-90.0f),1.0f,0.0f,0.0f));
		_root->addChild(_blowPos);
	}

	_bombBlowed = true;
	_timeSinceBlow = 0.0f;

}

void CBombManager::update(float delta, osg::Vec3 movement, osg::Vec3 wandPos, osg::Vec3 headPos)
{
	_timeSinceBlow += delta;
	if(_grabed==true && _bombBlowed==false)
	{
		_bomb->addMovement(movement);
	}

	if(_bombBlowed==true)
	{
		osg::ref_ptr<osg::Sphere> unitSphere;
		osg::ref_ptr<osg::ShapeDrawable> unitSphereDrawable;
		_blowGeode->removeDrawables(0,_blowGeode->getNumDrawables());
		unitSphere = new osg::Sphere( osg::Vec3(0.0f,0.0f,0.0f), _blowRadius*exp(_timeSinceBlow)*exp(_timeSinceBlow)*7);
		unitSphereDrawable = new osg::ShapeDrawable(unitSphere.get());
		_blowGeode->addDrawable(unitSphereDrawable.get());

		float vx = ((_bombPos[_nbBomb-1][0]>_joueur->getVirtual3Position().x())?_bombPos[_nbBomb-1][0]-_joueur->getVirtual3Position().x():_bombPos[_nbBomb-1][0]+_joueur->getVirtual3Position().x());
		float vy = ((_bombPos[_nbBomb-1][1]>_joueur->getVirtual3Position().y())?_bombPos[_nbBomb-1][1]-_joueur->getVirtual3Position().y():_bombPos[_nbBomb-1][1]+_joueur->getVirtual3Position().y());
		float vz = ((_bombPos[_nbBomb-1][2]>_joueur->getVirtual3Position().z())?_bombPos[_nbBomb-1][2]-_joueur->getVirtual3Position().z():_bombPos[_nbBomb-1][2]+_joueur->getVirtual3Position().z());
		if(_grabed==false)
		{
			// On regarde si la distance qui nous sépare de la bombe est inferieur au rayon du blow
//			osg::Vec3 v(_bombPos[_nbBomb-1][0]+_joueur->getVirtual3Position().x(),_bombPos[_nbBomb-1][1]-_joueur->getVirtual3Position().y(),_bombPos[_nbBomb-1][2]-_joueur->getVirtual3Position().z());
			float d = sqrt(vx*vx+vy*vy+vz*vz);
			if(d<=_blowRadius*exp(_timeSinceBlow)*exp(_timeSinceBlow)*7)
			{
				_joueur->setDeath(true);
			}
		}
		else
		{
//			osg::Vec3 v(headPos.x()-wandPos.x(),headPos.y()-wandPos.y(),headPos.z()-wandPos.z());
//			float d = sqrt(v.x()*v.x()+v.y()*v.y()+v.z()*v.z());
			float d = sqrt(vx*vx+vy*vy+vz*vz);
			if(d<=_blowRadius*exp(_timeSinceBlow)*15)
			{
				_joueur->setDeath(true);
			}
		}
	}
}

void CBombManager::grabBomb(osg::Vec3 wandPos)
{
	_grabed = true;
	_map->removeBomb();
	_root->addChild(_bomb->getNode());
	_bomb->setPosition(wandPos.x(),wandPos.y()-0.7,wandPos.z());
	_bomb->getNode()->computeBound();
	osg::MatrixTransform * rm = _bomb->getRotMatrix();
	rm->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(-90.0f),1.0f,0.0f,0.0f));
	rm->postMult(osg::Matrix::rotate(osg::DegreesToRadians(90.0f),0.0f,1.0f,0.0f));
}

bool CBombManager::checkIntersect(osg::Vec3 wandPos, osg::Vec3 headPos)
{
	// On calcul la position de la wand dans le monde
	osg::Vec3 wm = getWandPosInWorld(wandPos,headPos);
	_bomb->checkIntersect(wm);
	return _bomb->getSelected();
}

osg::Vec3 CBombManager::getWandPosInWorld(osg::Vec3 wandPos, osg::Vec3 headPos)
{
	return osg::Vec3(-((wandPos.x()-(headPos.x()-_joueur->getVirtual3Position().x()))),wandPos.y()-(headPos.y()-_joueur->getVirtual3Position().y()),wandPos.z()-(headPos.z()-_joueur->getVirtual3Position().z()));

}

osg::Vec3 CBombManager::getBombPosInWorld(osg::Vec3 wandPos, osg::Vec3 headPos)
{
	// On retourne la position de la bombe dans le monde
	return osg::Vec3(_bomb->getPosition().x(),_bomb->getPosition().y(),_bomb->getPosition().z());

}

bool CBombManager::checkDiffusable(osg::Vec3 wandPos, osg::Vec3 headPos)
{
	osg::Vec3 wm = getWandPosInWorld(wandPos,headPos);
	float a = 2.0f;
	float b = 2.0f;

	if(wm.x()>_safePoint.x()-a && wm.x()<_safePoint.x()+a
			&& wm.y()>_safePoint.y()-b && wm.y()<_safePoint.y()+b)
	{
		return true;
	}
	else
	{
		return false;
	}
}


osg::Drawable* CBombManager::createBeaconDrawable(const float & scale, osg::StateSet* bbState)
{
   float width = 1.0f;
   float height = 2.0f;

   width *= scale;
   height *= scale;

   osg::Geometry* shrubQuad = new osg::Geometry;

   osg::Vec3Array* shrubVerts = new osg::Vec3Array(4);
   (*shrubVerts)[0] = osg::Vec3(-width/2.0f, 0, 0);
   (*shrubVerts)[1] = osg::Vec3( width/2.0f, 0, 0);
   (*shrubVerts)[2] = osg::Vec3( width/2.0f, 0, height);
   (*shrubVerts)[3] = osg::Vec3(-width/2.0f, 0, height);

   shrubQuad->setVertexArray(shrubVerts);

   osg::Vec2Array* shrubTexCoords = new osg::Vec2Array(4);
   (*shrubTexCoords)[0].set(0.0f,0.0f);
   (*shrubTexCoords)[1].set(1.0f,0.0f);
   (*shrubTexCoords)[2].set(1.0f,1.0f);
   (*shrubTexCoords)[3].set(0.0f,1.0f);
   shrubQuad->setTexCoordArray(0,shrubTexCoords);

   shrubQuad->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));

   // Need to assign a color to the underlying geometry, otherwise we'll get
   // whatever color is current applied to our geometry.
   // Create a color array, add a single color to use for all the vertices

   osg::Vec4Array* colorArray = new osg::Vec4Array;
   colorArray->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f) ); // white, fully opaque

   // An index array for assigning vertices to colors (based on index in the array)
   osg::TemplateIndexArray
	  <unsigned int, osg::Array::UIntArrayType,4,1> *colorIndexArray;
   colorIndexArray =
	  new osg::TemplateIndexArray<unsigned int, osg::Array::UIntArrayType,4,1>;
   colorIndexArray->push_back(0);

   // Use the index array to associate the first entry in our index array with all
   // of the vertices.
   shrubQuad->setColorArray( colorArray);
   shrubQuad->setColorIndices(colorIndexArray);
   shrubQuad->setColorBinding(osg::Geometry::BIND_OVERALL);

   shrubQuad->setStateSet(bbState);

   return shrubQuad;
}
